# -*- mode: python; coding: utf-8; -*-
#---------------------------------------------------------------------------##
#
# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
# Copyright (C) 2003 Mt. Hood Playing Card Co.
# Copyright (C) 2005-2010 Skomoroh
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#---------------------------------------------------------------------------##
__docformat__ = "restructuredtext en"

import logging

Logger = logging.getLogger('PySolFC.StackShisensho')

from ..gamelib import state
from .. import comparecards as cmp_cards
from .. import hint
from .. import settings as c
from . import mahjongg as mstack
from ..pilelib import move as a_move

CONF_OPT = c.CONF_OPT


class Shisen_Hint(hint.AbstractHint):
    TOP_MATCHING = False

    def compute_hints(self):
        """
        compute_hints() -> None

        compute hints - main hint intelligence
        """
        rows = self._stacks['set']['rows']
        # get free stacks
        stacks = [s for s in rows if s.cards]
        # find matching tiles
        i = 0
        for r in stacks:
            for t in stacks[i + 1:]:
                if r.cardsBuild(t, t.cards):
                    # simple scoring...
                    if self.TOP_MATCHING:
                        score = 2000 - r.rown - t.rown
                    else:
                        score = 1000 + r.rown + t.rown
                    self.add_hint(score, 1, r, t)
            i += 1


class NotShisen_Hint(Shisen_Hint):
    TOP_MATCHING = True


class RowStack(mstack.RowStack):

    def cardsBlock(self):
        return False

    def _sequence(self, cards):
        return cmp_cards.isSameMahjonggRank(cards)

    def cardsBuild(self, from_stack, cards):
        if not super(RowStack, self).cardsBuild(from_stack, cards):
            return False

        game_cols = self.game.cols
        cols = len(game_cols)
        rows = len(game_cols[0])
        to_x, to_y = [i + 1 for i in self.position]
        from_x, from_y = [i + 1 for i in from_stack.position]
        dx, dy = from_x - to_x, from_y - to_y

        a = [[5] * (rows + 2) for i in range(cols + 2)]

        def can_move(cx, cy, nx, ny, cdir, ndir, count):
            """
            :Parameters:
              cx : int
                current left x position
              cy : int
                current top y position
              nx : int
                new left x position
              ny : int
                new top y position
              cdir : int
                current direction [0: none, 1: down, 2: up, 3: right, 4: left]
              ndir : int
                new direction [0: none, 1: down, 2: up, 3: right, 4: left]
              count : int
                num changes in direction
            """
            # check new coords is from_stack
            if (nx, ny,) == (from_x, from_y):
                return True
            # check new coords is an edge
            if any([nx < 0, ny < 0, nx > cols + 1, ny > rows + 1]):
                return True
            # check new coords is next to edge or empty
            if any([nx in (0, cols + 1), ny in (0, rows + 1),
                    not game_cols[nx - 1][ny - 1].cards]):
                if count == 0:  # No dir change
                    return True
                elif count == 1:  # 1st dir change
                    if cdir != ndir:
                        return any([ndir == 1 and dy > 0,
                                    ndir == 2 and dy < 0,
                                    ndir == 3 and dx > 0,
                                    ndir == 4 and dx < 0])
                    else:
                        return True
                elif count == 2:  # 2nd dir change
                    if cdir != ndir:
                        return any([ndir in (1, 2,) and cx == from_x,
                                    ndir in (3, 4,) and cy == from_y])
                    else:
                        return any([ndir == 1 and cy < from_y,
                                    ndir == 2 and cy > from_y,
                                    ndir == 3 and cx < from_x,
                                    ndir == 4 and cx > from_x])
                elif count == 3:  # 3rd dir change
                    return cdir == ndir
            return False

        res_path = [None]

        def do_accepts(cx, cy, cdir=0, count=0, path=[]):
            """
            :Parameters:
              cx : int
                current left x position
              cy : int
                current top y position
              dirc : int
                current direction [0: none, 1: down, 2: up, 3: right, 4: left]
              count : int
                num changes in direction
              path : list
                set of possible positions to from_stack
            """
            if a[cx][cy] < count:
                return
            a[cx][cy] = count
            if (cx, cy,) == (from_x, from_y,):
                res_path[0] = path
                return

            if can_move(cx, cy, cx, cy + 1, cdir, 1, count):  # moves down
                p = path[:]
                if cdir == 1:
                    nc = count
                else:
                    nc = count + 1
                    p.append((cx, cy,))
                do_accepts(cx, cy + 1, 1, nc, p)

            if can_move(cx, cy, cx, cy - 1, cdir, 2, count):  # moves up
                p = path[:]
                if cdir == 2:
                    nc = count
                else:
                    nc = count + 1
                    p.append((cx, cy,))
                do_accepts(cx, cy - 1, 2, nc, p)

            if can_move(cx, cy, cx + 1, cy, cdir, 3, count):  # moves right
                p = path[:]
                if cdir == 3:
                    nc = count
                else:
                    nc = count + 1
                    p.append((cx, cy,))
                do_accepts(cx + 1, cy, 3, nc, p)

            if can_move(cx, cy, cx - 1, cy, cdir, 4, count):  # moves left
                p = path[:]
                if cdir == 4:
                    nc = count
                else:
                    nc = count + 1
                    p.append((cx, cy,))
                do_accepts(cx - 1, cy, 4, nc, p)

        do_accepts(to_x, to_y)

        if a[from_x][from_y] > 3:
            return None

        res_path = res_path[0]
        res_path.append((from_x, from_y))
        return res_path

    def _mark_move(self, to_stack, ncards):
        from ..hint import AHINT
        game = self.game
        items = game._hint_item_(self, AHINT(from_stack=self, to_stack=to_stack,
                                      ncards=ncards), width=7,
                                      fill=CONF_OPT['colors']['hintarrow'])
        game.canvas.update_idletasks()
        seconds = CONF_OPT['timeouts']['hint']
        game.top.sleep(seconds)
        items.delete()

    def move_pile(self, ncards, to_stack, **kw):
        assert ncards == 1 and to_stack in self.game.s['rows']
        if to_stack.cards:
            assert ncards == 1
            game = self.game
            if not game.is_demo and CONF_OPT['shisen_show_hint']:
                self._mark_move(to_stack, ncards)
            game._move_do(a_move.ADropPairMove(self, to_stack, None, **kwargs),
                          reset=True)  # low level
            self.fill_stack()
            to_stack.fill_stack()
        else:
            super(RowStack, self).move_pile(ncards, to_stack, **kw)


class NotShisen_RowStack(RowStack):

    def cardsBuild(self, from_stack, cards):
        if not self.game.cardsMatch(self.cards[0], cards[-1]):
            return False
        if self.coln != from_stack.coln and self.rown != from_stack.rown:
            return True
        return [(self.coln + 1, self.rown + 1),
                (from_stack.coln + 1, from_stack.rown + 1)]